From 365190fe31cbc1ad886b73f3f583140cbec1ed8b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 3 Sep 2014 11:34:26 -0700 Subject: [PATCH] Implement manifest profiles For documentation, see the included documentation in the commit. --- src/cargo/ops/cargo_rustc/mod.rs | 15 +++- src/cargo/util/toml.rs | 108 ++++++++++++++++++++--------- src/doc/source/manifest.md | 39 +++++++++++ tests/test_cargo_compile.rs | 2 +- tests/test_cargo_cross_compile.rs | 2 +- tests/test_cargo_profiles.rs | 111 ++++++++++++++++++++++++++++++ tests/tests.rs | 1 + 7 files changed, 242 insertions(+), 36 deletions(-) create mode 100644 tests/test_cargo_profiles.rs diff --git a/src/cargo/ops/cargo_rustc/mod.rs b/src/cargo/ops/cargo_rustc/mod.rs index 61f19df97..8d342fa7d 100644 --- a/src/cargo/ops/cargo_rustc/mod.rs +++ b/src/cargo/ops/cargo_rustc/mod.rs @@ -242,7 +242,7 @@ fn prepare_rustc(package: &Package, target: &Target, crate_types: Vec<&str>, cx: &Context, req: PlatformRequirement) -> CargoResult> { let base = process("rustc", package, cx); - let base = build_base_args(base, target, crate_types.as_slice()); + let base = build_base_args(cx, base, target, crate_types.as_slice()); let target_cmd = build_plugin_args(base.clone(), cx, package, target, KindTarget); let plugin_cmd = build_plugin_args(base, cx, package, target, KindPlugin); @@ -303,7 +303,7 @@ fn rustdoc(package: &Package, target: &Target, }) } -fn build_base_args(mut cmd: ProcessBuilder, +fn build_base_args(cx: &Context, mut cmd: ProcessBuilder, target: &Target, crate_types: &[&str]) -> ProcessBuilder { let metadata = target.get_metadata(); @@ -317,7 +317,16 @@ fn build_base_args(mut cmd: ProcessBuilder, cmd = cmd.arg("--crate-type").arg(*crate_type); } - let profile = target.get_profile(); + // Despite whatever this target's profile says, we need to configure it + // based off the profile found in the root package's targets. + let mut profile = target.get_profile().clone(); + let root_package = cx.get_package(cx.resolve.root()); + for target in root_package.get_manifest().get_targets().iter() { + let root_profile = target.get_profile(); + if root_profile.get_env() != profile.get_env() { continue } + profile = profile.opt_level(root_profile.get_opt_level()) + .debug(root_profile.get_debug()); + } if profile.get_opt_level() != 0 { cmd = cmd.arg("--opt-level").arg(profile.get_opt_level().to_string()); diff --git a/src/cargo/util/toml.rs b/src/cargo/util/toml.rs index a268e6b70..c90d31883 100644 --- a/src/cargo/util/toml.rs +++ b/src/cargo/util/toml.rs @@ -4,6 +4,7 @@ use std::io::fs; use std::os; use std::slice; use std::str; +use std::default::Default; use toml; use semver; use serialize::{Decodable, Decoder}; @@ -198,6 +199,7 @@ pub struct DetailedTomlDependency { pub struct TomlManifest { package: Option>, project: Option>, + profile: Option, lib: Option>, bin: Option>, example: Option>, @@ -207,6 +209,21 @@ pub struct TomlManifest { dev_dependencies: Option>, } +#[deriving(Decodable, Clone, Default)] +pub struct TomlProfiles { + test: Option, + doc: Option, + bench: Option, + dev: Option, + release: Option, +} + +#[deriving(Decodable, Clone, Default)] +pub struct TomlProfile { + opt_level: Option, + debug: Option, +} + #[deriving(Decodable)] pub enum ManyOrOne { Many(Vec), @@ -411,12 +428,14 @@ impl TomlManifest { }; // Get targets + let profiles = self.profile.clone().unwrap_or(Default::default()); let targets = normalize(lib.as_slice(), bins.as_slice(), examples.as_slice(), tests.as_slice(), benches.as_slice(), - &metadata); + &metadata, + &profiles); if targets.is_empty() { debug!("manifest has no build targets"); @@ -572,38 +591,61 @@ fn normalize(libs: &[TomlLibTarget], examples: &[TomlExampleTarget], tests: &[TomlTestTarget], benches: &[TomlBenchTarget], - metadata: &Metadata) -> Vec { + metadata: &Metadata, + profiles: &TomlProfiles) -> Vec { log!(4, "normalizing toml targets; lib={}; bin={}; example={}; test={}, benches={}", libs, bins, examples, tests, benches); enum TestDep { Needed, NotNeeded } - fn target_profiles(target: &TomlTarget, dep: TestDep) -> Vec { - let mut ret = vec![Profile::default_dev(), Profile::default_release()]; + fn merge(profile: Profile, toml: &Option) -> Profile { + let toml = match *toml { + Some(ref toml) => toml, + None => return profile, + }; + let opt_level = toml.opt_level.unwrap_or(profile.get_opt_level()); + let debug = toml.debug.unwrap_or(profile.get_debug()); + profile.opt_level(opt_level).debug(debug) + } + + fn target_profiles(target: &TomlTarget, profiles: &TomlProfiles, + dep: TestDep) -> Vec { + let mut ret = vec![ + merge(Profile::default_dev(), &profiles.dev), + merge(Profile::default_release(), &profiles.release), + ]; match target.test { - Some(true) | None => ret.push(Profile::default_test()), + Some(true) | None => { + ret.push(merge(Profile::default_test(), &profiles.test)); + } Some(false) => {} } let doctest = target.doctest.unwrap_or(true); match target.doc { Some(true) | None => { - ret.push(Profile::default_doc().doctest(doctest)); + ret.push(merge(Profile::default_doc().doctest(doctest), + &profiles.doc)); } Some(false) => {} } match target.bench { - Some(true) | None => ret.push(Profile::default_bench()), + Some(true) | None => { + ret.push(merge(Profile::default_bench(), &profiles.bench)); + } Some(false) => {} } match dep { Needed => { - ret.push(Profile::default_test().test(false)); - ret.push(Profile::default_doc().doc(false)); - ret.push(Profile::default_bench().test(false)); + ret.push(merge(Profile::default_test().test(false), + &profiles.test)); + ret.push(merge(Profile::default_doc().doc(false), + &profiles.doc)); + ret.push(merge(Profile::default_bench().test(false), + &profiles.bench)); } _ => {} } @@ -616,7 +658,7 @@ fn normalize(libs: &[TomlLibTarget], } fn lib_targets(dst: &mut Vec, libs: &[TomlLibTarget], - dep: TestDep, metadata: &Metadata) { + dep: TestDep, metadata: &Metadata, profiles: &TomlProfiles) { let l = &libs[0]; let path = l.path.clone().unwrap_or_else(|| { TomlString(format!("src/{}.rs", l.name)) @@ -627,7 +669,7 @@ fn normalize(libs: &[TomlLibTarget], vec![if l.plugin == Some(true) {Dylib} else {Lib}] }); - for profile in target_profiles(l, dep).iter() { + for profile in target_profiles(l, profiles, dep).iter() { let mut metadata = metadata.clone(); // Libs and their tests are built in parallel, so we need to make // sure that their metadata is different. @@ -641,14 +683,14 @@ fn normalize(libs: &[TomlLibTarget], } fn bin_targets(dst: &mut Vec, bins: &[TomlBinTarget], - dep: TestDep, metadata: &Metadata, + dep: TestDep, metadata: &Metadata, profiles: &TomlProfiles, default: |&TomlBinTarget| -> String) { for bin in bins.iter() { let path = bin.path.clone().unwrap_or_else(|| { TomlString(default(bin)) }); - for profile in target_profiles(bin, dep).iter() { + for profile in target_profiles(bin, profiles, dep).iter() { let metadata = if profile.is_test() { // Make sure that the name of this test executable doesn't // conflicts with a library that has the same name and is @@ -668,19 +710,21 @@ fn normalize(libs: &[TomlLibTarget], } fn example_targets(dst: &mut Vec, examples: &[TomlExampleTarget], + profiles: &TomlProfiles, default: |&TomlExampleTarget| -> String) { for ex in examples.iter() { let path = ex.path.clone().unwrap_or_else(|| TomlString(default(ex))); - let profile = &Profile::default_test().test(false); + let profile = Profile::default_test().test(false); + let profile = merge(profile, &profiles.test); dst.push(Target::example_target(ex.name.as_slice(), &path.to_path(), - profile)); + &profile)); } } fn test_targets(dst: &mut Vec, tests: &[TomlTestTarget], - metadata: &Metadata, + metadata: &Metadata, profiles: &TomlProfiles, default: |&TomlTestTarget| -> String) { for test in tests.iter() { let path = test.path.clone().unwrap_or_else(|| { @@ -692,16 +736,17 @@ fn normalize(libs: &[TomlLibTarget], let mut metadata = metadata.clone(); metadata.mix(&format!("test-{}", test.name)); - let profile = &Profile::default_test().harness(harness); + let profile = Profile::default_test().harness(harness); + let profile = merge(profile, &profiles.test); dst.push(Target::test_target(test.name.as_slice(), &path.to_path(), - profile, + &profile, metadata)); } } fn bench_targets(dst: &mut Vec, benches: &[TomlBenchTarget], - metadata: &Metadata, + metadata: &Metadata, profiles: &TomlProfiles, default: |&TomlBenchTarget| -> String) { for bench in benches.iter() { let path = bench.path.clone().unwrap_or_else(|| { @@ -713,11 +758,12 @@ fn normalize(libs: &[TomlLibTarget], let mut metadata = metadata.clone(); metadata.mix(&format!("bench-{}", bench.name)); - let profile = &Profile::default_bench().harness(harness); + let profile = Profile::default_bench().harness(harness); + let profile = merge(profile, &profiles.bench); dst.push(Target::bench_target(bench.name.as_slice(), - &path.to_path(), - profile, - metadata)); + &path.to_path(), + &profile, + metadata)); } } @@ -731,25 +777,25 @@ fn normalize(libs: &[TomlLibTarget], match (libs, bins) { ([_, ..], [_, ..]) => { - lib_targets(&mut ret, libs, Needed, metadata); - bin_targets(&mut ret, bins, test_dep, metadata, + lib_targets(&mut ret, libs, Needed, metadata, profiles); + bin_targets(&mut ret, bins, test_dep, metadata, profiles, |bin| format!("src/bin/{}.rs", bin.name)); }, ([_, ..], []) => { - lib_targets(&mut ret, libs, Needed, metadata); + lib_targets(&mut ret, libs, Needed, metadata, profiles); }, ([], [_, ..]) => { - bin_targets(&mut ret, bins, test_dep, metadata, + bin_targets(&mut ret, bins, test_dep, metadata, profiles, |bin| format!("src/{}.rs", bin.name)); }, ([], []) => () } - example_targets(&mut ret, examples, + example_targets(&mut ret, examples, profiles, |ex| format!("examples/{}.rs", ex.name)); - test_targets(&mut ret, tests, metadata, + test_targets(&mut ret, tests, metadata, profiles, |test| { if test.name.as_slice() == "test" { "src/test.rs".to_string() @@ -757,7 +803,7 @@ fn normalize(libs: &[TomlLibTarget], format!("tests/{}.rs", test.name) }}); - bench_targets(&mut ret, benches, metadata, + bench_targets(&mut ret, benches, metadata, profiles, |bench| { if bench.name.as_slice() == "bench" { "src/bench.rs".to_string() diff --git a/src/doc/source/manifest.md b/src/doc/source/manifest.md index c000066a0..9e70b5988 100644 --- a/src/doc/source/manifest.md +++ b/src/doc/source/manifest.md @@ -93,6 +93,45 @@ You can specify the source of a dependency in one of two ways at the moment: Soon, you will be able to load packages from the Cargo registry as well. +# The `[profile.*]` Sections + +Cargo supports custom configuration of how rustc is invoked through **profiles** +at the top level. Any manifest may declare a profile, but only the **top level** +project's profiles are actually read. All dependencies' profiles will be +overridden. This is done so the top-level project has control over how its +dependencies are compiled. + +There are five currently supported profile names, all of which have the same +configuration available to them. Listed below is the configuration available, +along with the defaults for each profile. + +```toml +# The development profile, used for `cargo build` +[profile.dev] +opt-level = 0 # Controls the --opt-level the compiler builds with +debug = true # Controls whether the compiler passes -g or `--cfg ndebug` + +# The release profile, used for `cargo build --release` +[profile.release] +opt-level = 3 +debug = false + +# The testing profile, used for `cargo test` +[profile.test] +opt-level = 0 +debug = true + +# The benchmarking profile, used for `cargo bench` +[profile.bench] +opt-level = 3 +debug = false + +# The documentation profile, used for `cargo doc` +[profile.doc] +opt-level = 0 +debug = true +``` + # The `[dev-dependencies.*]` Sections The format of this section is equivalent to `[dependencies.*]`. Dev-dependencies diff --git a/tests/test_cargo_compile.rs b/tests/test_cargo_compile.rs index cb32c1bab..ace545bc2 100644 --- a/tests/test_cargo_compile.rs +++ b/tests/test_cargo_compile.rs @@ -1153,7 +1153,7 @@ test!(verbose_build { .file("src/lib.rs", ""); assert_that(p.cargo_process("build").arg("-v"), execs().with_status(0).with_stdout(format!("\ -{running} `rustc {dir}{sep}src{sep}lib.rs --crate-name test --crate-type lib \ +{running} `rustc {dir}{sep}src{sep}lib.rs --crate-name test --crate-type lib -g \ -C metadata=[..] \ -C extra-filename=-[..] \ --out-dir {dir}{sep}target \ diff --git a/tests/test_cargo_cross_compile.rs b/tests/test_cargo_cross_compile.rs index ab5c2ec59..13c1c6663 100644 --- a/tests/test_cargo_cross_compile.rs +++ b/tests/test_cargo_cross_compile.rs @@ -295,7 +295,7 @@ test!(linker_and_ar { .arg("-v"), execs().with_status(101) .with_stdout(format!("\ -{running} `rustc src/foo.rs --crate-name foo --crate-type bin \ +{running} `rustc src/foo.rs --crate-name foo --crate-type bin -g \ --out-dir {dir}{sep}target{sep}{target} \ --dep-info [..] \ --target {target} \ diff --git a/tests/test_cargo_profiles.rs b/tests/test_cargo_profiles.rs new file mode 100644 index 000000000..e51ad49c7 --- /dev/null +++ b/tests/test_cargo_profiles.rs @@ -0,0 +1,111 @@ +use std::os; +use std::path; + +use support::{project, execs}; +use support::{COMPILING, RUNNING}; +use hamcrest::assert_that; + +fn setup() { +} + +test!(profile_overrides { + let mut p = project("foo"); + p = p + .file("Cargo.toml", r#" + [package] + + name = "test" + version = "0.0.0" + authors = [] + + [profile.dev] + opt-level = 1 + debug = false + "#) + .file("src/lib.rs", ""); + assert_that(p.cargo_process("build").arg("-v"), + execs().with_status(0).with_stdout(format!("\ +{running} `rustc {dir}{sep}src{sep}lib.rs --crate-name test --crate-type lib \ + --opt-level 1 \ + --cfg ndebug \ + -C metadata=[..] \ + -C extra-filename=-[..] \ + --out-dir {dir}{sep}target \ + --dep-info [..] \ + -L {dir}{sep}target \ + -L {dir}{sep}target{sep}deps` +{compiling} test v0.0.0 ({url})\n", +running = RUNNING, compiling = COMPILING, sep = path::SEP, +dir = p.root().display(), +url = p.url(), +))); +}) + +test!(top_level_overrides_deps { + let mut p = project("foo"); + p = p + .file("Cargo.toml", r#" + [package] + + name = "test" + version = "0.0.0" + authors = [] + + [profile.release] + opt-level = 1 + debug = true + + [dependencies.foo] + path = "foo" + "#) + .file("src/lib.rs", "") + .file("foo/Cargo.toml", r#" + [package] + + name = "foo" + version = "0.0.0" + authors = [] + + [profile.release] + opt-level = 0 + debug = false + + [lib] + name = "foo" + crate_type = ["dylib", "rlib"] + "#) + .file("foo/src/lib.rs", ""); + assert_that(p.cargo_process("build").arg("-v").arg("--release"), + execs().with_status(0).with_stdout(format!("\ +{running} `rustc {dir}{sep}foo{sep}src{sep}lib.rs --crate-name foo \ + --crate-type dylib --crate-type rlib \ + --opt-level 1 \ + -g \ + -C metadata=[..] \ + -C extra-filename=-[..] \ + --out-dir {dir}{sep}target{sep}release{sep}deps \ + --dep-info [..] \ + -L {dir}{sep}target{sep}release{sep}deps \ + -L {dir}{sep}target{sep}release{sep}deps` +{running} `rustc {dir}{sep}src{sep}lib.rs --crate-name test --crate-type lib \ + --opt-level 1 \ + -g \ + -C metadata=[..] \ + -C extra-filename=-[..] \ + --out-dir {dir}{sep}target{sep}release \ + --dep-info [..] \ + -L {dir}{sep}target{sep}release \ + -L {dir}{sep}target{sep}release{sep}deps \ + --extern foo={dir}{sep}target{sep}release{sep}deps/\ + {prefix}foo-[..]{suffix} \ + --extern foo={dir}{sep}target{sep}release{sep}deps/libfoo-[..].rlib` +{compiling} foo v0.0.0 ({url}) +{compiling} test v0.0.0 ({url})\n", + running = RUNNING, + compiling = COMPILING, + dir = p.root().display(), + url = p.url(), + sep = path::SEP, + prefix = os::consts::DLL_PREFIX, + suffix = os::consts::DLL_SUFFIX).as_slice())); +}) diff --git a/tests/tests.rs b/tests/tests.rs index c84e20633..95ef99315 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -37,4 +37,5 @@ mod test_cargo_compile_plugins; mod test_cargo_doc; mod test_cargo_freshness; mod test_cargo_generate_lockfile; +mod test_cargo_profiles; mod test_cargo_package; -- 2.30.2